package com.situ.service.impl;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.situ.config.JwtProperties;
import com.situ.context.BaseContext;
import com.situ.domain.dto.AddManagerDTO;
import com.situ.domain.dto.LogInDTO;
import com.situ.domain.dto.ManagerPageQueryDTO;
import com.situ.domain.entity.RoleManagerKey;
import com.situ.domain.entity.TManager;
import com.situ.domain.vo.BasePageQueryVO;
import com.situ.domain.vo.ManagerListVO;
import com.situ.ex.UsernameDuplicatedException;
import com.situ.exception.ManagerException;
import com.situ.mapper.TManagerMapper;
import com.situ.service.ManagerService;
import com.situ.service.RoleManagerService;
import com.situ.utils.JwtUtil;
import com.situ.utils.SaltCodeUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
@RequiredArgsConstructor
@Slf4j
public class ManagerServiceImpl extends ServiceImpl<TManagerMapper, TManager> implements ManagerService {

    private final RoleManagerService roleManagerService;

    private final TManagerMapper managerMapper;

    /**
     * 登录
     *
     * @param logInDTO
     * @return
     */
    @Override
    public String login(LogInDTO logInDTO) {
        TManager manager = this.lambdaQuery()
                .eq(TManager::getUsername, logInDTO.getUsername())
                .one();
        if (manager == null) {
            throw new ManagerException("用户名或密码错误");
        }
        if (manager.getIsDelete() == 1) {
            throw new ManagerException("用户已注销");
        }
        // TODO 判断角色状态是否启用
        System.out.println(getMdtPassword(
                logInDTO.getPassword(),
                logInDTO.getUsername(),
                manager.getSalt()
        ).toUpperCase());
        System.out.println(manager.getPassword());
        boolean isSuccess = manager.getPassword().equals(
                getMdtPassword(
                        logInDTO.getPassword(),
                        logInDTO.getUsername(),
                        manager.getSalt()
                ).toUpperCase()
        );
        if (!isSuccess) {
            throw new ManagerException("用户名或密码错误");
        }
        StpUtil.login(manager.getId());
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
        return tokenInfo.getTokenValue();
    }


    /**
     * 获取管理员列表
     *
     * @return
     */
    @Override
    public BasePageQueryVO<ManagerListVO> getManagerList(ManagerPageQueryDTO pageQueryDTO) {
        Page<TManager> page = Page.of(pageQueryDTO.getCurrentPage(), pageQueryDTO.getPageSize());
        this.lambdaQuery()
                .like(StrUtil.isNotBlank(pageQueryDTO.getQueryString()), TManager::getUsername, pageQueryDTO.getQueryString())
                .or()
                .like(StrUtil.isNotBlank(pageQueryDTO.getQueryString()), TManager::getEmail, pageQueryDTO.getQueryString())
                .page(page)
                .getRecords();
        List<ManagerListVO> list = page.getRecords().stream().map(manager -> {
            ManagerListVO managerListVO = new ManagerListVO();
            BeanUtil.copyProperties(manager, managerListVO);
            return managerListVO;
        }).toList();
        return BasePageQueryVO
                .<ManagerListVO>builder()
                .currentPage(page.getCurrent())
                .pageSize(page.getSize())
                .pageTotal(page.getTotal())
                .pages(page.getPages())
                .list(list)
                .build();
    }

    /**
     * 添加管理员
     *
     * @param managerDTO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addManager(AddManagerDTO managerDTO) {
        TManager one = this.lambdaQuery()
                .eq(TManager::getUsername, managerDTO.getUsername())
                .one();
        if (one != null) {
            throw new UsernameDuplicatedException("用户名已存在");
        }
        String saltCode = SaltCodeUtils.generateSaltCode(8);
        TManager manager = new TManager();
        manager.setUsername(managerDTO.getUsername());
        manager.setPassword(getMdtPassword(managerDTO.getPassword(), managerDTO.getUsername(), saltCode).toUpperCase());
        manager.setSalt(saltCode);
        manager.setAvatar("http://124.156.204.126:9000/hello-minio/defaultImage..jpg");
        manager.setIsDelete(0);
        manager.setCreatedUser(BaseContext.getCurrentId().toString());
        manager.setCreatedTime(new Date());
        this.save(manager);
        // TODO 这里为什么不能主键回写？
        Integer id = this.lambdaQuery()
                .eq(TManager::getUsername, managerDTO.getUsername())
                .one().getId();
        log.info("添加管理员:{}", manager);
        managerDTO.getRoleIds().forEach(roleId -> {
            roleManagerService.save(RoleManagerKey
                    .builder()
                    .managerId(id)
                    .roleId(roleId)
                    .build());
        });
    }

    /**
     * 根据用户id获取权限列表
     * @param i
     * @return
     */
    @Override
    public List<String> getPermissionListByUserId(Integer loginId) {
        return managerMapper.getPermissionListByUserId(loginId);
    }

    @Override
    public List<String> getRoleListByUserId(Integer loginId) {
        return managerMapper.getRoleListByUserId(loginId);
    }

    /**
     * 获取MD5密码
     *
     * @param password
     * @param username
     * @param salt     盐值
     * @return
     */
    private String getMdtPassword(String password,
                                  String username,
                                  String salt) {
        for (int i = 0; i < 3; i++) {
            String newPs = salt + password + username;
            password = DigestUtil.md5Hex(newPs);
        }
        return password;
    }
}
